package org.walkerljl.commons.image.group;

import org.walkerljl.commons.config.ConfiguratorFactory;
import org.walkerljl.commons.config.impl.readonly.PropertiesConfiguratorProvider;
import org.walkerljl.commons.format.Scanf;
import org.walkerljl.commons.image.group.enums.GroupCondition;
import org.walkerljl.commons.log.Logger;
import org.walkerljl.commons.log.LoggerFactory;
import org.walkerljl.commons.thread.NamedThreadFactory;
import org.walkerljl.commons.util.StringUtils;
import org.walkerljl.commons.util.ValidateUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ImageGroupBootstrap
 *
 * @author lijunlin
 */
public class ImageGroupBootstrap {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImageGroupBootstrap.class);

    private String srcImagesPath;
    private String destImagesPath;
    private GroupCondition groupCondition;
    private int maxQuantitiesPerDestDirectory = 100;
    private int processThreadQuantities = 200;
    private BlockingQueue<File> taskQueue = new ArrayBlockingQueue<File>(processThreadQuantities * 3);
    private Map<String, File> destDirectoryMap = new HashMap<String, File>();
    private CountDownLatch finishLatch;

    public static void main(String[] args) throws Exception {
        try {
            ConfiguratorFactory.getInstance().bind(new PropertiesConfiguratorProvider("conf/image/config.properties"));
            ImageGroupBootstrap bootstrap = new ImageGroupBootstrap();
            System.out.println("Please input src images path:");
            bootstrap.setSrcImagesPath(Scanf.readLine());
            System.out.println("Please input grouped dest images path:");
            bootstrap.setDestImagesPath(Scanf.readLine());
            System.out.println("Please input max file quantities per dest directory:");
            String maxQuantitiesPerDestDirectoryStr = Scanf.readLine();
            if (StringUtils.isNotBlank(maxQuantitiesPerDestDirectoryStr)) {
                bootstrap.setMaxQuantitiesPerDestDirectory(Integer.parseInt(maxQuantitiesPerDestDirectoryStr));
            }
            System.out.println("Please input process thread quantities:");
            String processThreadQuantitiesStr = Scanf.readLine();
            if (StringUtils.isNotBlank(processThreadQuantitiesStr)) {
                bootstrap.setProcessThreadQuantities(Integer.parseInt(processThreadQuantitiesStr));
            }
            System.out.println("Please input group condition value(group by address:1,time of year:2,time of month:3,time of day:4):");
            String groupConditionStr = Scanf.readLine();
            if (!ValidateUtils.validateNumber(groupConditionStr)) {
                int retryTimes = 2;
                while (retryTimes-- > 1) {
                    groupConditionStr = Scanf.readLine();
                    if (ValidateUtils.validateNumber(groupConditionStr)) {
                        break;
                    }
                }
            }
            if (!ValidateUtils.validateNumber(groupConditionStr)) {
                System.out.println(String.format("Group condition value is invalid:%s.", groupConditionStr));
                return;
            }
            bootstrap.setGroupCondition(GroupCondition.parseType(Integer.parseInt(groupConditionStr)));
            System.out.println(String.format("Run config:src images path:%s,dest images path:%s,max file quantities per dest directory:%s,process thread quantities:%s,group condition:%s.",
                    bootstrap.getSrcImagesPath(), bootstrap.getDestImagesPath(), bootstrap.getMaxQuantitiesPerDestDirectory(),
                    bootstrap.getProcessThreadQuantities(), bootstrap.getGroupCondition()));

            System.out.println("Enter 'yes' or 'y' to run.");
            String confirmStr = Scanf.readLine();
            if (confirmStr.equalsIgnoreCase("y") || confirmStr.equalsIgnoreCase("yes")) {
                System.out.println("Running...");
                bootstrap.process();
                bootstrap.getFinishLatch().await();
            }

            System.out.println("Done...");
        } catch (Throwable e) {
            LOGGER.error(e);
        }
    }

    public void process() {
        NamedThreadFactory namedThreadFactory = new NamedThreadFactory("orgwalkerljl-commons-imagegroup");
        finishLatch = new CountDownLatch(processThreadQuantities + 1);

        namedThreadFactory.newThread(new GroupProducer(srcImagesPath, taskQueue, finishLatch)).start();

        AtomicInteger index = new AtomicInteger(0);
        GroupConsumer consumer = new GroupConsumer(taskQueue, groupCondition, destImagesPath,
                index, maxQuantitiesPerDestDirectory, destDirectoryMap, finishLatch);
        for (int i = 0; i < processThreadQuantities; i++) {
            namedThreadFactory.newThread(consumer).start();
        }
    }

    public String getSrcImagesPath() {
        return srcImagesPath;
    }

    public void setSrcImagesPath(String srcImagesPath) {
        this.srcImagesPath = srcImagesPath;
    }

    public String getDestImagesPath() {
        return destImagesPath;
    }

    public void setDestImagesPath(String destImagesPath) {
        this.destImagesPath = destImagesPath;
    }

    public GroupCondition getGroupCondition() {
        return groupCondition;
    }

    public void setGroupCondition(GroupCondition groupCondition) {
        this.groupCondition = groupCondition;
    }

    public int getProcessThreadQuantities() {
        return processThreadQuantities;
    }

    public void setProcessThreadQuantities(int processThreadQuantities) {
        this.processThreadQuantities = processThreadQuantities;
    }

    public int getMaxQuantitiesPerDestDirectory() {
        return maxQuantitiesPerDestDirectory;
    }

    public void setMaxQuantitiesPerDestDirectory(int maxQuantitiesPerDestDirectory) {
        this.maxQuantitiesPerDestDirectory = maxQuantitiesPerDestDirectory;
    }

    public CountDownLatch getFinishLatch() {
        return finishLatch;
    }

    public void setFinishLatch(CountDownLatch finishLatch) {
        this.finishLatch = finishLatch;
    }
}
